home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Night Owl 6
/
Night Owl's Shareware - PDSI-006 - Night Owl Corp (1990).iso
/
001a
/
mskrmsrc.zip
/
MSNTCP.C
< prev
next >
Wrap
C/C++ Source or Header
|
1991-10-24
|
48KB
|
1,955 lines
/* File MSNTCP.C
* Main TCP protocol code
*
* Copyright (C) 1991, University of Waterloo.
* Copyright (C) 1991, Trustees of Columbia University in the
* City of New York. Permission is granted to any individual or
* institution to use, copy, or redistribute this software as long as
* it is not sold for profit and this copyright notice is retained.
*
* Original version created by Erick Engelke of the University of
* Waterloo, Waterloo, Ontario, Canada.
* Adapted and modified for MS-DOS Kermit by Joe R. Doupnik,
* Utah State University, jrd@cc.usu.edu, jrd@usu.Bitnet.
*
* Last edit
* 6 Sept 1991
*
* PCTCP - the true worker of Waterloo TCP
* - contains all opens, closes, major read/write routines and
* basic IP handler for incomming packets
* - NOTE: much of the TCP/UDP/IP layering is done at data structure
* level, not in separate routines or tasks
*
*/
#include "msntcp.h"
#include "msnlib.h"
/*
#define OLD 1
*/
static void (*system_yield)() = NULL;
static tcp_Retransmitter();
static tcp_unthread(void * s);
static tcp_handler(void * s);
static udp_handler(void * s);
extern int arp_handler(in_Header *);
extern int icmp_handler(void * s);
int tcp_rst(in_Header *his_ip, tcp_Header *oldtcpp);
static tcp_ProcessData(void *s, void * tp, int len);
static initialized = 0;
longword ipbcast = 0xffffffffL; /* default IP broadcast address */
word waittime = 2; /* tcp_sendsoon, normal ticks to wait, ~0.1 sec */
#ifdef DEBUG
void (*dbugxmit)() = NULL;
void (*dbugrecv)() = NULL;
#endif
/*
* Local IP address
*/
longword my_ip_addr = 0L; /* for external references */
longword sin_mask = 0xfffffe00L;
longword sin_gate = 0L;
/*
* IP identification numbers
*/
static int ip_id = 0; /* packet number */
static int next_tcp_port = 1024; /* auto incremented */
static int next_udp_port = 1024;
static tcp_Socket *tcp_allsocs = NULL; /* TCP socket linked list head */
static udp_Socket *udp_allsocs = NULL; /* UDP socket linked list head */
/* Timer definitions */
#define RETRAN_STRAT_TIME 1 /* ticks, how often to check retransmitter*/
#define tcp_RETRANSMITTIME 3 /* interval to call retransmitter */
#define tcp_LONGTIMEOUT 31 /* timeout for opens */
#define tcp_TIMEOUT 13 /* timeout during a connection */
word debug_on = 0;
byte *hostname = "012345678901234567890123456789012345678901234567890";
word mss = ETH_MSS;
word sourcequench = 0; /* non-zero if received ICMP Source Quench */
/*
* tcp_Init - Initialize the tcp implementation
* - may be called more than once without hurting
*/
int
tcp_init()
{
extern int arp_last_gateway;
extern int last_nameserver;
extern byte *loc_domain;
void icmp_init();
if (initialized != 0) return (1); /* success, inited already */
/* initialize ethernet interface */
arp_init(); /* clear ARP tables */
icmp_init(); /* clear ICMP material */
last_nameserver = 0; /* reset the nameserver table */
loc_domain = NULL; /* name server work variable */
*hostname = 0; /* reset the host's name */
tcp_allsocs = NULL;
udp_allsocs = NULL;
ip_id = 0;
initialized = eth_init(); /* init the hardware, can fail */
if (my_ip_addr == 0L) /* my_ap_addr is longword*/
bcopy(ð_addr[2], &my_ip_addr, 4); /* a pseudo long */
next_udp_port = next_tcp_port = 1024 + (realclock() & 0x1ff);
eth_free (NULL); /* clear all pkt rcv bufs */
return (initialized); /* success (1) or failure (0) */
}
/*
* Shut down the card and all services
*/
void
tcp_shutdown()
{
while (tcp_allsocs != NULL)
tcp_abort(tcp_allsocs);
eth_release();
initialized = 0;
}
int
udp_open(s, lport, ina, port, datahandler)
udp_Socket *s;
longword ina;
word lport, port;
procref datahandler;
{
if (s == NULL) return (0);
memset(s, 0, sizeof(udp_Socket));
s->ip_type = UDP_PROTO;
if (lport == 0) lport = ++next_udp_port; /* get a nonzero port val */
s->myport = lport;
/* check for broadcast */
if (ina == 0xffffffffL || ina == 0L || ina == ipbcast)
memset(s->hisethaddr, 0xff, sizeof(eth_address));
else
if (arp_resolve(ina, &s->hisethaddr[0]) == 0)
return (0);
s->hisaddr = ina;
s->hisport = port;
s->dataHandler = datahandler;
s->usr_yield = system_yield;
s->next = udp_allsocs;
udp_allsocs = s;
return (1);
}
/*
* Actively open a TCP connection to a particular destination.
* - 0 on error
*/
int
tcp_open(s, lport, ina, port, datahandler)
tcp_Socket *s;
longword ina;
word lport, port;
procref datahandler;
{
if (s == NULL) return (0);
memset(s, 0, sizeof(tcp_Socket)); /* zero everything */
s->ip_type = TCP_PROTO;
s->mss = mss;
s->state = tcp_StateSYNSENT;
s->timeout = set_timeout(tcp_LONGTIMEOUT);
s->rmaxdatalen = tcp_MaxBufSize;
s->cwindow = 1; /* slow start VJ algorithm */
s->vj_sa = 4; /* about 250 ms (4/18.2 tics/sec) */
if (lport == 0)
lport = ++next_tcp_port; /* get a nonzero port value */
s->myport = lport;
if (! arp_resolve(ina, &s->hisethaddr[0]))
return (0);
s->hisaddr = ina;
s->hisport = port;
s->seqnum = intel(set_timeout(0)) & 0xffff0000;
s->datalen = 0;
s->flags = tcp_FlagSYN;
s->unhappy = TRUE;
s->dataHandler = datahandler;
s->usr_yield = system_yield;
s->next = tcp_allsocs;
s->rtt_delay = s->rtt_smooth = 18; /* one second startup */
tcp_allsocs = s;
return (tcp_send(s)); /* fail if send fails */
}
/*
* Passive open: listen for a connection on a particular port
*/
tcp_listen(s, lport, ina, port, datahandler, timeout)
tcp_Socket *s;
word port, lport, timeout;
longword ina;
procref datahandler;
{
if (s == NULL) return (0);
memset(s, 0, sizeof(tcp_Socket));
s->ip_type = TCP_PROTO;
s->mss = mss;
s->rmaxdatalen = tcp_MaxBufSize;
s->cwindow = 1; /* slow start VJ algorithm */
s->vj_sa = 4; /* about 250 ms */
s->state = tcp_StateLISTEN;
if (timeout == 0)
s->timeout = 0; /* no timeout */
else
s->timeout = set_timeout(timeout);
s->myport = lport;
s->hisport = port;
s->hisaddr = ina;
s->seqnum = intel(set_timeout(0)) & 0xffff0000;
s->datalen = 0;
s->flags = 0;
s->unhappy = FALSE;
s->dataHandler = datahandler;
s->usr_yield = system_yield;
s->next = tcp_allsocs;
tcp_allsocs = s;
return (1);
}
static int
udp_close(ds)
udp_Socket *ds;
{
register udp_Socket *sp;
sp = udp_allsocs; /* ptr to first socket */
if (sp == NULL) return (0); /* failure */
if (sp == ds) udp_allsocs = ds->next; /* if we are first, unlink */
while (sp != NULL)
{
if (sp->next == ds) /* if current points to us */
{
sp->next = ds->next; /* point it to our successor */
break;
}
sp = sp->next; /* look at next socket */
}
return (1); /* success */
}
/*
* Send a FIN on a particular port -- only works if it is open
* Must still allow receives
* Returns 0 for failure
*/
static
tcp_close(s)
tcp_Socket *s;
{
if (s == NULL) return (0);
if (s->ip_type != TCP_PROTO)
return (0); /* failure */
if (s->state == tcp_StateESTAB || s->state == tcp_StateSYNREC)
{
if (s->err_msg == NULL)
s->err_msg = "TCP_Closed called";
s->flags = tcp_FlagACK | tcp_FlagFIN;
if (s->datalen)
s->flags |= tcp_FlagPUSH;
s->state = tcp_StateFINWT1;
s->timeout = set_timeout(4);
/* should be a pretty lengthy time */
s->unhappy = TRUE;
tcp_send(s);
return (1); /* success */
}
return (0);
}
/*
* Abort a tcp connection
* Returns 0 if failure
*/
static int
tcp_abort(s)
tcp_Socket *s;
{
if (s == NULL) return (0); /* failure */
if (s->err_msg == NULL) s->err_msg = "TCP_ABORT";
if ((s->state != tcp_StateLISTEN) && (s->state != tcp_StateCLOSED))
{
s->flags = tcp_FlagRST | tcp_FlagACK;
s->unhappy = TRUE;
tcp_send(s);
}
s->unhappy = FALSE;
s->datalen = 0;
s->state = tcp_StateCLOSED;
if (s->dataHandler) s->dataHandler(s, 0, -1);
tcp_unthread(s);
return (1); /* success */
}
void
sock_abort(s)
tcp_Socket *s;
{
if (s == NULL) return; /* do nothing */
if (s->ip_type == TCP_PROTO)
tcp_abort(s);
else
udp_close(s);
}
/*
* tcp_sendsoon - schedule a transmission pretty soon
* - this one has an imperfection at midnight, but it
* is not significant to the connection performance
* Return 0 if failure.
*/
tcp_sendsoon(s)
tcp_Socket *s;
{
longword temp;
if (s == NULL) return (0);
if (s->ip_type != TCP_PROTO) return (0);
s->karn_count = 1;
if (sourcequench != 0) /* ICMP shutup message */
{
waittime += waittime; /* double the timeout */
sourcequench = 0; /* forget the reminder */
}
temp = set_ttimeout(waittime); /* Bios ticks into the future */
if ((s->rtt_time < temp)) /* if now is too soon */
return (1); /* then wait some more */
if (waittime > 2) /* if backing off already */
waittime = (waittime * 15) / 16; /* recover slowly */
s->rtt_time = temp;
return (1);
}
/*
* Retransmitter - called periodically to perform tcp retransmissions
* Returns 0 if failure
*/
static
tcp_Retransmitter()
{
register tcp_Socket *s;
static longword retran_strat = 0L; /* timeout retran strategy */
/* do this only once per RETRAN_STRAT_TIME clock ticks */
if (!chk_timeout(retran_strat))
return (0L);
retran_strat = set_ttimeout(RETRAN_STRAT_TIME);
for (s = tcp_allsocs; s != NULL; s = s->next)
{
if ((s->datalen > 0) || (s->unhappy == TRUE) ||
(s->karn_count == 1))
/* retransmission strategy */
if (chk_timeout(s->rtt_time)) /* if timeout */
{
if (s->karn_count == 0)
s->karn_count = 2;
/* if really did timeout */
tcp_send(s);
}
if (s->timeout && chk_timeout(s->timeout))
{
if (s->state == tcp_StateTIMEWT)
{
s->flags = tcp_FlagACK;
s->state = tcp_StateCLOSED;
}
else if (s->state != tcp_StateESTAB)
{
s->err_msg = "Timeout, aborting";
tcp_abort(s);
}
}
}
return (1); /* success */
}
/*
* Unthread a socket from the socket list, if it's there. Return 0 on failure
*/
static int
tcp_unthread(ds)
tcp_Socket *ds;
{
register tcp_Socket *sp;
if (ds == NULL) return (0); /* failure */
if ((ds->rdatalen == 0) || (ds->state > tcp_StateESTAB))
ds->ip_type = 0; /* fail io */
ds->state = tcp_StateCLOSED; /* tcp_tick needs this */
if (tcp_allsocs == NULL) return (0);
sp = tcp_allsocs; /* ptr to first socket */
if (sp == ds) tcp_allsocs = ds->next; /* if we are first, unlink */
while (sp != NULL)
{
if (sp->next == ds) /* if current points to us */
{
sp->next = ds->next; /* point it to our successor */
break;
}
sp = sp->next; /* look at next socket */
}
return (1); /* success */
}
/*
* tcp_tick - called periodically by user application
* - returns 0 when our socket closes
* - called with socket parameter or NULL
*/
tcp_tick(s)
sock_type *s;
{
register in_Header *ip;
int packettype;
/* finish off dead sockets */
if (s != NULL)
if (s->tcp.ip_type == TCP_PROTO &&
s->tcp.state == tcp_StateCLOSED)
s->tcp.ip_type = 0;
/* read a packet */
while ((ip = (in_Header *)eth_arrived(&packettype)) != NULL)
{
switch (intel16(packettype)) /* big to little endian form */
{
case 0x0800: /* do IP */
if (checksum(ip, in_GetHdrlenBytes(ip)) != 0xffff)
{
if (debug_on)
outs("IP Received BAD Checksum \n\r");
break;
}
if ((my_ip_addr == 0L) ||
(intel(ip->destination) == my_ip_addr))
{
switch (ip->proto)
{
case TCP_PROTO:
tcp_handler(ip);
break;
case UDP_PROTO:
udp_handler(ip);
break;
case ICMP_PROTO:
icmp_handler(ip);
break;
default:
break;
} /* end of switch (ip->proto) */
}
break;
case 0x0806: /* do ARP */
arp_handler(ip);
break;
case 0x8035: /* do RARP */
rarp_handler(ip);
break;
default: /* unknown type */
break;
} /* end of switch */
eth_free(ip); /* free the processed packet */
} /* end of while */
tcp_Retransmitter(); /* check on pending sends */
return ((s != NULL)? s->tcp.ip_type: 0); /* 0 means closed socket */
}
void
tcp_set_debug_state(x)
word x;
{
debug_on = x;
}
/* returns 1 if connection is established, else 0 */
int
tcp_established(s)
tcp_Socket *s;
{
if (s == NULL) return (0); /* failure */
return (s->state == tcp_StateESTAB);
}
static int
udp_Write(s, datap, len)
udp_Socket *s;
byte *datap;
int len;
{
tcp_PseudoHeader ph;
struct pkt
{
in_Header in;
udp_Header udp;
int data;
/* longword maxsegopt; */
} register *pkt;
if (s == NULL || datap == NULL)
return (0); /* failure */
pkt = (struct pkt *)eth_formatpacket(&s->hisethaddr[0], 0x0008);
pkt->in.length = intel16(sizeof(in_Header) + UDP_LENGTH + len);
/* UDP header */
pkt->udp.srcPort = intel16(s->myport);
pkt->udp.dstPort = intel16(s->hisport);
pkt->udp.checksum = 0;
pkt->udp.length = intel16(UDP_LENGTH + len);
bcopy(datap, &pkt->data, len);
/* Internet header */
pkt->in.hdrlen_ver = 0x45; /* version 4, hdrlen 5 */
pkt->in.tos = 0;
pkt->in.identification = intel16(++ip_id); /* was post inc */
pkt->in.frag = 0;
#ifdef KERMIT
pkt->in.ttl = 60;
#else
pkt->in.ttl = 254;
#endif
pkt->in.proto = UDP_PROTO; /* UDP */
/* inp->ttlProtocol = (250<<8) + 6; */
pkt->in.checksum = 0;
pkt->in.source = intel(my_ip_addr);
pkt->in.destination = intel(s->hisaddr);
pkt->in.checksum = ~checksum(&pkt->in, sizeof(in_Header));
/* compute udp checksum if desired */
if (s->soc_mode & UDP_MODE_NOCHK)
pkt->udp.checksum = 0;
else
{
ph.src = pkt->in.source; /* big endian now */
ph.dst = pkt->in.destination;
ph.mbz = 0;
ph.protocol = UDP_PROTO; /* UDP */
ph.length = pkt->udp.length; /* big endian now */
ph.checksum = checksum(&pkt->udp, intel16(ph.length));
pkt->udp.checksum = ~checksum(&ph, sizeof(ph));
}
#ifdef DEBUG
if (dbugxmit) (*dbugxmit)(s,inp,udpp);
#endif
if (eth_send(intel16(pkt->in.length)) != 0) /* send pkt */
return (len);
else return (0); /* failed */
}
/*
* udp_read - read data from buffer, does large buffering.
* Return 0 on failure.
*/
static int
udp_read(s, datap, maxlen)
udp_Socket *s;
byte *datap;
int maxlen;
{
register int x;
if (s == NULL || datap == NULL) return (0); /* failure */
if ((x = s->rdatalen) > 0)
{
if (x > maxlen) x = maxlen;
if (x > 0)
{
bcopy(s->rdata, datap, x);
if (s->rdatalen -= x)
bcopy(&s->rdata[x], s->rdata, s->rdatalen);
}
}
return (x);
}
udp_cancel(in_Header *ip)
{
int len;
register udp_Header *up;
register udp_Socket *s;
/* match to a udp socket */
len = in_GetHdrlenBytes(ip);
up = (udp_Header *)((byte *)ip + len); /* udp frame pointer */
/* demux to active sockets */
for (s = udp_allsocs; s != NULL; s = s->next)
if (s->hisport != 0 &&
intel16(up->dstPort) == s->myport &&
intel16(up->srcPort) == s->hisport &&
intel(ip->source) == s->hisaddr)
break;
if (s == NULL) /* demux to passive sockets */
for (s = udp_allsocs; s != NULL; s = s->next)
if (s->hisport == 0 &&
intel16(up->dstPort) == s->myport)
break;
if (s != NULL)
{
s->rdatalen = 0;
s->ip_type = 0;
}
return (1); /* success */
}
int
tcp_cancel(in_Header *ip)
{
int len;
register tcp_Socket *s;
register tcp_Header *tp;
if (ip == NULL) return (0); /* failure */
len = in_GetHdrlenBytes(ip); /* check work */
tp = (tcp_Header *)((byte *)ip + len); /* TCP frame pointer */
/* demux to active sockets */
for (s = tcp_allsocs; s != NULL; s = s->next)
if (s->hisport != 0 &&
intel16(tp->dstPort) == s->myport &&
intel16(tp->srcPort) == s->hisport &&
intel(ip->source) == s->hisaddr)
break;
if (s == NULL) /* demux to passive sockets */
for (s = tcp_allsocs; s != NULL; s = s->next)
if (s->hisport == 0 &&
intel16(tp->dstPort) == s->myport)
break;
if (s != NULL)
{
s->rdatalen = 0;
s->state = tcp_StateCLOSED;
s->ip_type = 0;
if (s->dataHandler) s->dataHandler(s, 0, -1);
tcp_unthread(s);
}
return (1); /* success */
}
static int
tcp_read(s, datap, maxlen)
tcp_Socket *s;
byte *datap;
int maxlen;
{
register int x;
if (s == NULL || datap == NULL || maxlen == 0)
return (0); /* failure or read nothing */
if ((x = s->rdatalen) > 0)
{
if (x > maxlen) x = maxlen;
bcopy(s->rdata, datap, x); /* copy out desired data */
if ((s->rdatalen -= x) < 0)
s->rdatalen = 0; /* readjust socket buf */
bcopy(&s->rdata[x], s->rdata, s->rdatalen);
tcp_sendsoon(s); /* update the window */
}
return (x); /* return bytes read */
}
/*
* Write data to a connection.
* Returns number of bytes written, == 0 when connection is not in
* established state.
*/
static int
tcp_Write(s, dp, len)
tcp_Socket *s;
byte *dp;
int len;
{
register int x;
if (s == NULL || dp == NULL) return (0); /* failure */
if (s->state != tcp_StateESTAB) len = 0;
if (len > (x = tcp_MaxBufSize - s->datalen)) len = x;
if (len > 0)
{
bcopy(dp, &s->data[s->datalen], len);
s->datalen += len;
s->unhappy = TRUE;/* redundant because of outstanding data */
if (s->soc_mode & TCP_MODE_NONAGLE)
tcp_send(s);
else /* transmit if first data or reached MTU */
/* not true MTU, but better than nothing */
if ((s->datalen == len) || (s->datalen > (s->mss)))
tcp_send(s);
}
return (len);
}
/*
* Send pending data
*/
static int
tcp_Flush(s)
tcp_Socket *s;
{
if (s == NULL) return (0); /* failure */
if (s->datalen > 0)
{
s->flags |= tcp_FlagPUSH;
tcp_send(s);
}
return (1); /* success */
}
/*
* Handler for incoming UDP packets. Return 0 if no socket.
*/
static int
udp_handler(ip)
in_Header *ip;
{
register udp_Header *up;
register udp_Socket *s;
tcp_PseudoHeader ph;
byte *dp;
word len;
len = in_GetHdrlenBytes(ip);
up = (udp_Header *)((byte *)ip + len); /* UDP segment pointer */
len = intel16(up->length);
/* demux to active sockets */
for (s = udp_allsocs; s != NULL; s = s->next)
if (s->hisport != 0 &&
intel16(up->dstPort) == s->myport &&
intel16(up->srcPort) == s->hisport &&
intel(ip->source) == s->hisaddr)
break;
#ifdef DEBUG
if (dbugrecv) (*dbugrecv)(s,ip,up);
#endif
if (s == NULL) /* demux to passive sockets */
for (s = udp_allsocs; s != NULL; s = s->next)
if (s->hisaddr == 0 &&
intel16(up->dstPort) == s->myport)
{
if (arp_resolve(intel(ip->source),
&s->hisethaddr[0]))
{
s->hisaddr = intel(ip->source);
s->hisport = intel16(up->srcPort);
}
break;
}
if (s == NULL) /* demux to broadcast sockets */
for (s = udp_allsocs; s != NULL; s = s->next)
if (s->hisaddr == ipbcast &&
intel16(up->dstPort) == s->myport)
break;
if (s == NULL)
{
if (debug_on) outs("discarding...");
return (0); /* say no socket */
}
if (up->checksum)
{
ph.src = ip->source; /* already bigend'd */
ph.dst = ip->destination;
ph.mbz = 0;
ph.protocol = UDP_PROTO;
ph.length = up->length;
ph.checksum = checksum(up, len);
if (checksum(&ph, sizeof(tcp_PseudoHeader)) != 0xffff)
return (0); /* failure */
}
/* process user data */
if ((len -= UDP_LENGTH) > 0)
{
dp = (byte *)(up);
if (s->dataHandler != NULL)
s->dataHandler(s, &dp[UDP_LENGTH], len, &ph);
else
{
if (len > udp_MaxBufSize) len = udp_MaxBufSize;
bcopy(&dp[UDP_LENGTH], s->rdata, len);
s->rdatalen = len;
}
}
return (1); /* success */
}
/* Handle TCP packets. Return 0 if no socket. */
static
tcp_handler(ip)
in_Header *ip;
{
register tcp_Header *tp;
tcp_PseudoHeader ph;
int len;
int diff; /* signed, please */
register tcp_Socket *s;
word flags;
long diffticks, ldiff; /* must be signed */
len = in_GetHdrlenBytes(ip);
tp = (tcp_Header *)((byte *)ip + len); /* tcp frame pointer */
len = intel16(ip->length) - len; /* len of tcp data */
flags = intel16(tp->flags);
/* demux to active sockets */
for (s = tcp_allsocs; s != NULL; s = s->next)
if (s->hisport != 0 &&
intel16(tp->dstPort) == s->myport &&
intel16(tp->srcPort) == s->hisport &&
intel(ip->source) == s->hisaddr)
break;
if (s == NULL) /* demux to passive sockets, must be a new session */
for (s = tcp_allsocs; s != NULL; s = s->next)
if ((s->hisport == 0) &&
(intel16(tp->dstPort) == s->myport))
break;
#ifdef DEBUG
if (dbugrecv) (*dbugrecv)(s, ip, tp);
#endif
if (s == NULL)
{
/* no session seems to exist, nor is there desire to start
one, so we must send a reset */
tcp_rst(ip, tp);
return (0); /* 0 to say socket is closed */
}
/* save his ethernet address */
bcopy(&((((eth_Header *)ip) - 1)->source[0]), &s->hisethaddr[0],
sizeof(eth_address));
ph.src = ip->source; /* network order now */
ph.dst = ip->destination;
ph.mbz = 0;
ph.protocol = TCP_PROTO;
ph.length = intel16(len);
ph.checksum = checksum(tp, len);
if (checksum(&ph, sizeof(ph)) != 0xffff)
{
if (debug_on) outs("bad tcp checksum \n\r");
return (1);
}
/* reset code */
if (flags & tcp_FlagRST)
{
if (debug_on) outs("\7\7connection reset\n");
s->rdatalen = 0;
s->state = tcp_StateCLOSED;
if (s->dataHandler) s->dataHandler(s, 0, -1);
tcp_unthread(s);
return (0); /* say socket is closed */
}
/* update our retransmission stuff */
if (s->karn_count == 2)
{
/* use the backed off rto */
}
else if ((diffticks = set_ttimeout(0) - s->vj_last) >= 0)
{ /* we ignore the overnight case */
diffticks -= (s->vj_sa >> 3);
s->vj_sa += (word)diffticks;
if (diffticks < 0)
diffticks = -diffticks;
diffticks -= (s->vj_sd >> 2);
s->vj_sd += diffticks;
s->rto = ((s->vj_sa >> 2) + s->vj_sd) >> 1;
s->karn_count = 0;
}
if (flags & tcp_FlagRST)
{
/* we must listen to resync */
}
switch (s->state) {
case tcp_StateLISTEN: /* accepting SYNs */
if (flags & tcp_FlagSYN)
{
s->acknum = intel(tp->seqnum) + 1;
s->hisport = intel16(tp->srcPort);
s->hisaddr = intel(ip->source);
s->flags = tcp_FlagSYN | tcp_FlagACK;
tcp_send(s); /* we must respond immediately */
s->state = tcp_StateSYNREC;
s->unhappy = FALSE;
s->timeout = set_timeout(tcp_TIMEOUT);
}
else
tcp_rst(ip, tp); /* send a reset */
break;
case tcp_StateSYNSENT: /* added ACK Section */
if (flags & tcp_FlagSYN)
{
s->flags = tcp_FlagACK;
s->timeout = set_timeout(tcp_TIMEOUT);
/* FlagACK means connection established, else SYNREC */
if (flags & tcp_FlagACK)
{ /* but is it for the correct session? */
if (tp->acknum == intel(s->seqnum + 1))
{
s->state = tcp_StateESTAB;
s->seqnum++; /* good increment */
s->acknum = intel(tp->seqnum) + 1; /*32 bits*/
s->unhappy = TRUE;
tcp_ProcessData(s, tp, len);
}
else
{ /* wrong ack, force a RST and resend SYN*/
s->flags = tcp_FlagRST;
s->unhappy = TRUE;
tcp_send(s);
s->unhappy = FALSE; /* no need to retransmit */
s->flags = tcp_FlagSYN;
tcp_send(s);
}
}
else
{
s->acknum++;
s->state = tcp_StateSYNREC;
/* maybe check sequence number stuff too */
}
}
break;
case tcp_StateSYNREC: /* recSYNSENT, sentACK, waiting EST */
if (flags & tcp_FlagSYN)
{
s->flags = tcp_FlagSYN | tcp_FlagACK;
tcp_send(s);
s->timeout = set_timeout(tcp_TIMEOUT);
}
if ((flags & tcp_FlagACK) && (intel(tp->acknum) == (s->seqnum + 1)))
{
s->flags = tcp_FlagACK;
tcp_send(s);
s->seqnum++;
s->unhappy = FALSE;
s->state = tcp_StateESTAB;
s->timeout = 0; /* never timeout */
tcp_ProcessData(s, tp, len);
}
break;
case tcp_StateESTAB:
if ((flags & tcp_FlagACK) == 0) return (1); /* they must ack somthing*/
s->timeout = 0L; /* we do not timeout at this point */
/* process their ack value in packet */
/* If their ack preceeds the window this is an old pkt. */
/* If ack exceeds the window then they are lying, don't believe. */
/* However, in all cases grab any useful data for us. */
ldiff = intel(tp->acknum) - s->seqnum; /* current - prev ack */
if (ldiff >= 0 && ldiff <= s->datalen)
{ /* their ack is in our window*/
s->seqnum += ldiff; /* update ACK'd file pointer */
diff = ldiff; /* 16 bits, bigger than our window */
s->datalen -= diff; /* deduct amount ACK'd */
/* move down residual in send buf */
bcopy(&s->data[diff], s->data, s->datalen);
}
s->flags = tcp_FlagACK; /* tell them thanks */
tcp_ProcessData(s, tp, len); /* process our data in the packet */
if (ldiff != 0 || len != 0)
tcp_sendsoon(s);
break;
case tcp_StateFINWT1:
/* process ack value in packet */
ldiff = intel(tp->acknum) - s->seqnum; /* current - prev ack */
if (ldiff >= 0 && ldiff <= s->datalen) /* their ack is in our window*/
{
s->seqnum += ldiff; /* update ACK'd file counter */
diff = ldiff; /* 16 bits, more than our window */
s->datalen -= diff; /* deduct amount ACK'd */
bcopy(&s->data[diff], s->data, s->datalen); /* move residual */
}
tcp_ProcessData(s, tp, len); /* process our data in the packet */
if (flags & tcp_FlagFIN) /* trying to do simultaneous close */
{
if ((intel(tp->acknum) >= s->seqnum + 1) &&
(intel(tp->seqnum) == s->acknum))
{ /* other guy wishes to close too */
s->seqnum++;
s->acknum++;
s->flags = tcp_FlagACK;
s->unhappy = TRUE;
s->timeout = set_timeout(3);
s->state = tcp_StateCLOSING;
}
}
else if (flags & tcp_FlagACK)
/* other side is legitimately acking our fin */
if ((intel(tp->acknum) == s->seqnum + 1) &&
(intel(tp->seqnum) == s->acknum) &&
(s->datalen == 0))
{
s->seqnum++;
s->datalen = 0;
s->state = tcp_StateFINWT2;
s->timeout = set_timeout(3);
s->unhappy = FALSE;
}
break;
case tcp_StateFINWT2:
if ((flags & (tcp_FlagACK | tcp_FlagFIN)) ==
(tcp_FlagACK | tcp_FlagFIN))
if ((intel(tp->acknum) == s->seqnum) &&
(intel(tp->seqnum) == s->acknum))
{
s->acknum++;
s->flags = tcp_FlagACK;
tcp_send(s);
s->unhappy = FALSE;
s->timeout = set_timeout(2); /* max seg life 2 sec */
s->state = tcp_StateTIMEWT;
}
break;
case tcp_StateCLOSING:
if ((flags & (tcp_FlagACK | tcp_FlagFIN)) == tcp_FlagACK)
if ((tp->acknum == intel(s->seqnum)) &&
(tp->seqnum == intel(s->acknum))) /* all agree */
{
s->state = tcp_StateTIMEWT;
s->timeout = set_timeout(tcp_TIMEOUT);
s->unhappy = FALSE; /* no more retransmissions */
}
break;
case tcp_StateLASTACK:
if (flags & tcp_FlagFIN) /* they lost our two packets, back up */
{
s->flags = tcp_FlagACK;
tcp_send(s);
s->flags = tcp_FlagACK | tcp_FlagFIN;
tcp_send(s);
s->unhappy = FALSE;
}
else
{
if ((intel(tp->acknum) == (s->seqnum + 1)) &&
(intel(tp->seqnum) == s->acknum))
s->state = tcp_StateCLOSED; /* no 2 msl */
tcp_unthread(s);
return (1);
}
break;
case tcp_StateTIMEWT:
s->flags = tcp_FlagACK;
s->acknum = intel(tp->seqnum) + 1;
tcp_send(s);
break;
} /* end switch */
if (s->unhappy == TRUE) tcp_sendsoon(s);
return (1); /* success */
}
/*
* Process the data in an incoming packet.
* Called from all states where incoming data can be received: established,
* fin-wait-1, fin-wait-2
*/
static int
tcp_ProcessData(s, tp, len)
tcp_Socket *s;
tcp_Header *tp;
int len;
{
register int diff, x;
long ldiff; /* signed */
byte *dp;
word flags, *options, numoptions, opt_temp;
if (s == NULL || tp == NULL) return (0); /* failure */
s->window = intel16(tp->window);
flags = intel16(tp->flags);
ldiff = s->acknum - intel(tp->seqnum); /* signed long */
if (flags & tcp_FlagSYN) ldiff--; /* SYN counts as one unit */
diff = ldiff; /* 16 bit version */
/* find the data portion */
x = tcp_GetDataOffset(tp) << 2; /* quadword to byte format */
dp = (byte *)tp + x;
/* process those options */
if (numoptions = x - sizeof(tcp_Header))
{
options = (word *)(tp + sizeof(tcp_Header));
while (numoptions-- > 0)
switch (*options++)
{
case 0: numoptions = 0; /* end of options */
case 1: break; /* nop */
/* we are very liberal on MSS stuff */
case 2: if (*options == 2)
{
opt_temp = intel16(*(word*)(&options[1]));
if (opt_temp < s->mss)
s->mss = opt_temp;
}
numoptions -= 2 + *options;
options += *options;
break;
} /* end of switch and while */
} /* end of if */
/* done option processing */
len -= x; /* remove the header length */
if (diff >= 0) /* skip already received bytes */
{
dp += diff;
len -= diff;
if (s->dataHandler != NULL)
s->acknum += s->dataHandler(s, dp, len);
else
/* no handler, just dump to buffer, should be indexed, handles goofs */
/* limit receive size to our window */
if (s->rdatalen >= 0)
{
if (len > (x = tcp_MaxBufSize - s->rdatalen))
len = x;
if (len > 0)
{ /* new ack begins at end of data */
s->acknum += len;
bcopy(dp, &s->rdata[s->rdatalen], len);
s->rdatalen += len;
}
} /* end of if (s->datalen.. else */
} /* end of if (diff > 0) */
s->unhappy = (s->datalen != 0)? TRUE: FALSE;
if (flags & tcp_FlagFIN)
{
s->acknum++;
switch (s->state)
{
case tcp_StateESTAB:
s->err_msg = "Connection closed.";
/* tcp_StateCLOSWT ... here we go */
x = tcp_StateLASTACK;
s->flags |= tcp_FlagFIN;
s->unhappy = TRUE; /* to send packet */
break;
case tcp_StateFINWT1: /* on ack should go to finwt2 */
s->flags |= tcp_FlagFIN;
/* not certain of this addition */
x = tcp_StateCLOSING;
break;
case tcp_StateFINWT2:
x = tcp_StateTIMEWT;
break;
} /* end of switch (s->state) */
s->state = x;
} /* end of if (flags & tcp_FlagFIN) */
s->timeout = set_timeout(tcp_TIMEOUT);
return (1); /* success */
}
/*
* Format and send an outgoing segment
*/
static int
tcp_send(s)
tcp_Socket *s;
{
tcp_PseudoHeader ph;
struct pkt
{
in_Header in;
tcp_Header tcp;
word maxsegopt[2];
} register *pkt;
byte *dp;
int senddatalen, sendtotlen, sendpktlen;
register int ippkt; /* 1..s->cwindow */
if (s == NULL) return (0); /* failure */
pkt = (struct pkt *)eth_formatpacket(&s->hisethaddr[0], 0x0008);
dp = (byte *)pkt->maxsegopt; /* dp constant for multi-packet sends */
/* this is our total possible send size */
if (s->window > 0)
senddatalen = (s->datalen >= s->window)?
s->window - 1: s->datalen;
else senddatalen = 0;
sendtotlen = 0; /* running count of what we've sent */
/* step through our packets */
for (ippkt = 1; ippkt <= s->cwindow; ippkt++)
{ /* adjust size for each packet */
if (senddatalen > s->mss)
senddatalen = s->mss;
/* tcp header */
pkt->tcp.srcPort = intel16(s->myport);
pkt->tcp.dstPort = intel16(s->hisport);
pkt->tcp.seqnum = intel(s->seqnum + sendtotlen);
pkt->tcp.acknum = intel(s->acknum);
pkt->tcp.window = intel16(s->rmaxdatalen - s->rdatalen);
pkt->tcp.flags = intel16(s->flags | 0x5000);
pkt->tcp.checksum = 0;
pkt->tcp.urgentPointer = 0;
/* do options if this is our first packet */
if ((s->flags & (tcp_FlagSYN | tcp_FlagACK)) == tcp_FlagSYN)
{
sendpktlen = sizeof(tcp_Header) +
sizeof(in_Header) + 4;
pkt->tcp.flags = intel16(intel16(pkt->tcp.flags) +
0x1000);
pkt->maxsegopt[0] = 0x0402;
pkt->maxsegopt[1] = intel16(s->mss);
dp += 4;
}
else
{
if (senddatalen > 0) /* handle packets with data */
{
sendpktlen = senddatalen + sizeof(tcp_Header)
+ sizeof(in_Header);
bcopy(s->data, dp, senddatalen);
}
else /* handle no-data, not-first-SYN packets */
sendpktlen = sizeof(tcp_Header) + sizeof(in_Header);
}
/* internet header */
pkt->in.hdrlen_ver = 0x45; /* version 4, hdrlen 5 */
pkt->in.tos = 0;
pkt->in.identification = intel16(++ip_id); /* pre-inc req'd */
pkt->in.frag = 0;
#ifdef KERMIT
pkt->in.ttl = 60;
#else
pkt->in.ttl = 254;
#endif
pkt->in.proto = TCP_PROTO;
pkt->in.checksum = 0;
pkt->in.source = intel(my_ip_addr);
pkt->in.destination = intel(s->hisaddr);
pkt->in.length = intel16(sendpktlen);
pkt->in.checksum = ~checksum(&pkt->in, sizeof(in_Header));
/* compute tcp checksum */
ph.src = pkt->in.source; /* already in network order */
ph.dst = pkt->in.destination;
ph.mbz = 0;
ph.protocol = 6;
ph.length = intel16(sendpktlen - sizeof(in_Header));
ph.checksum = checksum(&pkt->tcp,
(sendpktlen - sizeof(in_Header) + 1) & 0xfffe);
pkt->tcp.checksum = ~checksum(&ph, sizeof(ph));
#ifdef DEBUG
if (dbugxmit) (*dbugxmit)(s, &pkt->in, &pkt->tcp);
#endif
if (eth_send(intel16(pkt->in.length)) == 0)
return (0); /* sending failed */
sendtotlen += senddatalen;
} /* do next ip pkt */
if (s->karn_count == 2)
{
if (s->rto) s->rto = (s->rto * 3) / 2;
else s->rto = 4;
}
else
{
s->vj_last = set_ttimeout(0);
s->karn_count = 0;
}
s->rtt_time = set_ttimeout(s->rto);
return (1); /* success */
}
/*
* Format and send a reset tcp packet
*/
int
tcp_rst(in_Header *his_ip, tcp_Header *oldtcpp)
{
tcp_PseudoHeader ph;
struct pkt
{
in_Header in;
tcp_Header tcp;
word maxsegopt[2];
} register *pkt;
eth_Header *eth;
register int sendtotlen; /* length of packet */
if (his_ip == NULL || oldtcpp == NULL) return (0); /* failure */
/* see RFC 793 page 65 for details */
if (oldtcpp->flags & tcp_FlagRST)
return (0);
if ((oldtcpp->flags & tcp_FlagACK) == 0)
{
oldtcpp->seqnum = 0;
oldtcpp->flags = tcp_FlagACK;
}
else
oldtcpp->flags = 0;
/* convoluted mechanism - reads his ethernet address or garbage */
eth = eth_hardware((byte *)his_ip);
pkt = (struct pkt *)eth_formatpacket(eth, 0x0008);
sendtotlen = sizeof(tcp_Header) + sizeof(in_Header);
pkt->in.length = intel16(sendtotlen);
/* TCP header */
pkt->tcp.srcPort = oldtcpp->dstPort;
pkt->tcp.dstPort = oldtcpp->srcPort;
pkt->tcp.seqnum = oldtcpp->seqnum;
pkt->tcp.acknum = oldtcpp->acknum;
pkt->tcp.window = 0;
pkt->tcp.flags = tcp_FlagRST | oldtcpp->flags;
pkt->tcp.checksum = 0;
pkt->tcp.urgentPointer = 0;
/* Internet header */
pkt->in.hdrlen_ver = 0x45; /* version 4, hdrlen 5 */
pkt->in.tos = 0;
pkt->in.identification = intel16(++ip_id); /* use pre-inc here */
pkt->in.frag = 0;
#ifdef KERMIT
pkt->in.ttl = 60; /* time to live */
#else
pkt->in.ttl = 254;
#endif
pkt->in.proto = TCP_PROTO;
pkt->in.checksum = 0;
pkt->in.source = his_ip->destination;
pkt->in.destination = his_ip->source;
pkt->in.checksum = ~checksum(&pkt->in, sizeof(in_Header));
/* compute TCP checksum */
ph.src = pkt->in.source; /* already big endian */
ph.dst = pkt->in.destination;
ph.mbz = 0;
ph.protocol = 6;
ph.length = intel16(sendtotlen - sizeof(in_Header));
ph.checksum = checksum(&pkt->tcp, (sendtotlen - sizeof(in_Header) + 1)
& 0xfffe);
pkt->tcp.checksum = ~checksum(&ph, sizeof(ph));
#ifdef DEBUG
if (dbugxmit) (*dbugxmit)(NULL, &pkt->in, &pkt->tcp);
#endif
return (eth_send(intel16(pkt->in.length)));
}
/**********************************************************************
* socket functions
**********************************************************************/
/* socket based procedures */
/*
* sock_yield - enable user defined yield function
*/
void
sock_yield(tcp_Socket *s, void (*fn)())
{
if (s != NULL)
s->usr_yield = fn;
else
system_yield = fn;
}
/*
* sock_mode - set binary or ascii - affects sock_gets, sock_dataready
* - set udp checksums
*/
int
sock_mode(tcp_Socket *s, word mode)
{
if (s != NULL)
{
s->soc_mode = (s->soc_mode & 0xfffc) | mode;
return (1); /* success */
}
else
return (0); /* failure */
}
/*
* sock_read - read a socket with maximum n bytes
* - busywaits until buffer is full but calls s->usr_yield
* - returns count also when connection gets closed
*/
int
sock_read(sock_type *s, byte *dp, int len)
{
register int templen, count;
count = 0;
if (s == NULL || dp == NULL) return (0); /* failure */
do
{
if (s->udp.ip_type == UDP_PROTO)
templen = udp_read(s, dp, len);
else
templen = tcp_read(s, dp, len);
if (templen = 0)
if (tcp_tick(s) == 0) return (count);
if (s->tcp.usr_yield != NULL) (s->tcp.usr_yield)();
count += templen;
len -= templen;
}
while (len > 0);
return (count);
}
/*
* sock_fastread - read a socket with maximum n bytes
* - does not busywait until buffer is full
*/
int
sock_fastread(sock_type *s, byte *dp, int len)
{
if (s == NULL || dp == NULL) return (0); /* failure */
if (s->udp.ip_type == UDP_PROTO)
len = udp_read(s, dp, len);
else
len = tcp_read(s, dp, len);
return (len);
}
/*
* sock_write - writes data and returns length written
* - does not perform flush
* - repeatedly calls s->usr_yield
*/
int
sock_write(sock_type *s, byte *dp, int len)
{
register int offset, oldlen;
oldlen = len;
offset = 0;
if (s == NULL || dp == NULL) return (0); /* failure */
while (len > 0)
{
if (s->udp.ip_type == UDP_PROTO)
offset += udp_Write(s, dp + offset, len);
else
offset += tcp_Write(s, dp + offset, len);
len = oldlen - offset;
if (s->udp.usr_yield != NULL) (s->udp.usr_yield)();
if (tcp_tick(s) == 0) return (0); /* no socket */
}
return (oldlen);
}
int
sock_fastwrite(sock_type *s, byte *dp, int len)
{
tcp_tick(NULL); /* updates our output buffer*/
if (s == NULL) return (0); /* failure */
return ((s->udp.ip_type == UDP_PROTO)?
udp_Write(s, dp, len):
tcp_Write(s, dp, len));
}
int
sock_flush(sock_type *s)
{
if (s == NULL) return (0); /* failure */
if (s->tcp.ip_type == TCP_PROTO)
tcp_Flush(s);
return (1);
}
/*
* sock_flushnext - cause next transmission to have a flush
*/
int
sock_flushnext(sock_type *s)
{
if (s == NULL) return (0); /* failure */
if (s->tcp.ip_type == TCP_PROTO)
s->tcp.flags |= tcp_FlagPUSH;
return (1); /* success */
}
/*
* sock_putc - put a character
* - no expansion but flushes on '\n'
* - returns character
*/
int
sock_putc(sock_type *s, byte c)
{
byte ch;
ch = c;
if (s == NULL) return (-1); /* failure */
if ((ch == '\n') || (ch == '\r'))
sock_flushnext(s);
sock_write(s, &ch, 1);
return (ch & 0xff);
}
int
sock_getc(sock_type *s)
{
byte ch;
if (s == NULL) return (-1); /* failure */
sock_read(s, &ch, 1);
return (ch & 0xff);
}
#ifndef KERMIT
/*
* sock_puts - does not append carriage return in binary mode
* - returns length
*/
int
sock_puts(sock_type *s, byte *dp)
{
register int len;
if (s == NULL || dp == NULL) return (0); /* failure */
len = strlen(dp);
sock_flushnext(s);
sock_write(s, dp, len);
if (s->tcp.soc_mode & TCP_MODE_ASCII)
sock_putc(s, '\n');
return (len);
}
#endif /* KERMIT */
/*
* sock_update - update the socket window size to the other guy
* Note: a better and safer criterion is s->rdatalen < mss
* which allows room for a full incoming segment without loss or overflow.
*/
static int
sock_update(tcp_Socket *s)
{
if (s == NULL) return (0); /* failure */
if (s->ip_type == TCP_PROTO)
{
if (s->rdatalen < (3 * s->rmaxdatalen) / 4)
tcp_send(s); /* update the window */
else
tcp_sendsoon(s);
}
return (1); /* success */
}
#ifndef KERMIT
/*
* sock_gets - read a string from any socket
* - return length of returned string
* - removes end of line terminator
*/
word
sock_gets(sock_type *s, byte *dp, int n)
{
int len, templen;
byte *src_p, *temp, *temp2;
word *np;
if (s == NULL || dp == NULL) return (0); /* failure */
if (s->udp.ip_type == UDP_PROTO) {
if (n > udp_MaxBufSize) n = udp_MaxBufSize;
src_p = s->udp.rdata;
np = &s->udp.rdatalen;
} else {
if (n > tcp_MaxBufSize) n = tcp_MaxBufSize;
src_p = s->tcp.rdata;
np = &s->tcp.rdatalen;
}
src_p[ *np ] = 0; /* terminate string */
strncpy(dp, src_p, n); /* copy everything */
dp[ n-1 ] = 0; /* terminate */
if (temp = strchr(dp, '\n')) *temp = 0;
if (temp2= strchr(dp, '\r')) *temp2= 0;
len = strlen(dp);
/* skip if there were no crs or lfs ??? */
if ((temp2 == NULL) && (temp == NULL) && (strlen(dp) < n - 1)) {
*dp = 0;
return (0);
}
/* skip over \n and \r but stop on end */
#ifndef OLD
if (temp) templen = temp - (byte *)dp;
else if (temp2) templen = temp2 - (byte *)dp;
else templen = len + 1;
if (templen) {
templen++;
bcopy(&src_p[ templen ], src_p, *np -= templen);
} else
*np = 0;
#else
temp = &src_p[ len + 1 ];
while (*temp && ((*temp == '\n') || (*temp == '\r')))
temp++;
if (*temp)
bcopy(temp, src_p, *np = strlen(temp));
else
*np = 0;
#endif /* OLD */
sock_update((tcp_Socket *)s); /* new window */
return (len);
}
#endif /* KERMIT */
/*
* sock_dataready - returns number of bytes waiting to be read
* - if in ASCII mode, return 0 until a line is present
* or the buffer is full
*/
word
sock_dataready(sock_type *s)
{
register int len;
register byte *p;
if (s == NULL) return (0); /* failure */
if ((len = s->tcp.rdatalen) == 0) return (0);
if (s->tcp.soc_mode & TCP_MODE_ASCII)
{
if (len == tcp_MaxBufSize)
return (tcp_MaxBufSize);
/* check for terminating \n \r */
p = s->tcp.rdata;
if ((strchr(p, '\n') == NULL) || (strchr(p, '\r') == NULL))
return (len);
return (0);
}
return (len);
}
int
sock_established(sock_type *s)
{
if (s == NULL) return (0); /* failure */
switch (s->tcp.ip_type)
{
case UDP_PROTO:
return (1);
case TCP_PROTO:
return (s->tcp.state == tcp_StateESTAB);
default:
return (0);
}
}
int
sock_close(s)
sock_type *s;
{
register int status;
if (s == NULL) return (0); /* failure */
switch (s->udp.ip_type)
{
case UDP_PROTO :
status = udp_close(s);
break;
case TCP_PROTO :
status = tcp_close(s);
tcp_tick(s);
break;
}
return (status); /* success */
}
/* return count of chars in socket receive buffer which lie before char ch */
word
sock_findch(sock_type *s, byte ch)
{
int i;
int len;
register byte c = ch;
register byte * p;
tcp_tick(s); /* update socket buffer */
if ((len = sock_dataready(s)) == 0) /* get qty available */
return (-1); /* say no char available */
p = s->tcp.rdata; /* point to start of socket buffer */
for (i = 0; i < len; i++) /* scan for byte ch */
if (*p++ == ch) break; /* stop when found */
return (i);
}
/*
* ip user level timer stuff
* void ip_timer_init(void *s, int delayseconds)
* int ip_timer_expired(void *s)
* - 0 if not expired
*/
void
ip_timer_init(s, delayseconds)
udp_Socket *s;
int delayseconds;
{
if (s == NULL)
return;
if (delayseconds != 0)
s->usertimer = set_timeout(delayseconds);
else
s->usertimer = 0;
}
int
ip_timer_expired(s)
udp_Socket *s;
{
if (s == NULL) return (1); /* say time out */
if (s->usertimer == 0) /* cannot expire */
return (0);
return (chk_timeout(s->usertimer));
}
long
make_timeout(word timeout)
{
if (timeout) return (set_timeout(timeout));
return (0);
}
/*
* check_timeout - test agains timeout clock - account for overflow
*/
int
check_timeout(unsigned long timeout)
{
if (timeout) return (chk_timeout(timeout));
return (0);
}
/*
* ip_delay0 called by macro sock_wait_established()
* ip_delay1 called by macro sock_wait_intput()
* ip_delay2 called by macro sock_wait_closed();
*
*/
ip_delay0(s, timeoutseconds, fn, statusptr)
sock_type *s;
int timeoutseconds;
procref fn;
int *statusptr;
{
register int status;
if (s == NULL)
{
status = -1; /* failure */
if (statusptr != NULL) *statusptr = status;
return (status);
}
ip_timer_init(s, timeoutseconds);
do
{
if (s->tcp.ip_type == TCP_PROTO)
if (tcp_established(s))
{
status = 0;
break;
}
if (tcp_tick(s) == 0)
{
s->tcp.err_msg = "Host refused connection";
status = -1; /* get an early reset */
break;
}
if (ip_timer_expired(s))
{
sock_close(s);
status = -1;
break;
}
if (fn != NULL) if (status = fn(s)) break;
if (s->tcp.usr_yield != NULL) (s->tcp.usr_yield)();
if (s->tcp.ip_type == UDP_PROTO)
{
status = 0;
break;
}
} while (1 == 1);
if (statusptr != NULL) *statusptr = status;
return (status);
}
int
ip_delay1(s, timeoutseconds, fn, statusptr)
sock_type *s;
int timeoutseconds;
procref fn;
int *statusptr;
{
register int status;
if (s == NULL)
{
status = -1; /* failure */
if (statusptr != NULL) *statusptr = status;
return (status);
}
ip_timer_init(s, timeoutseconds);
sock_flush(s); /* new enhancement */
do
{
if (sock_dataready(s))
{
status = 0;
break;
}
if (tcp_tick(s) == 0)
{
status = 1;
break;
}
if (ip_timer_expired(s))
{
sock_close(s);
status = -1;
break;
}
if (fn != NULL)
if (status = fn(s)) break;
if (s->tcp.usr_yield != NULL) (s->tcp.usr_yield)();
} while (1 == 1);
if (statusptr != NULL) *statusptr = status;
return (status);
}
int
ip_delay2(s, timeoutseconds, fn, statusptr)
sock_type *s;
int timeoutseconds;
procref fn;
int *statusptr;
{
register int status;
if (s == NULL)
{
status = 0; /* failure */
if (statusptr != NULL) *statusptr = status;
return (status);
}
ip_timer_init(s, timeoutseconds);
if (s->tcp.ip_type != TCP_PROTO) return (1);
do
{
if (tcp_tick(s) == 0) /* 0 means no socket */
{
status = 1;
break;
}
if (ip_timer_expired(s))
{
sock_abort(s);
status = 0;
break;
}
if (fn != NULL)
if (status = fn(s)) break;
if (s->tcp.usr_yield != NULL) (s->tcp.usr_yield)();
} while (1 == 1);
if (statusptr != NULL) *statusptr = status;
return (status);
}
byte *
rip(byte *s) /* put null terminator on CR or LF */
{
register byte *temp;
if (s == NULL) return (NULL); /* failure */
if (temp = strchr(s, '\n')) *temp = 0;
if (temp = strchr(s, '\r')) *temp = 0;
return (s);
}